package edu.whu.rose.clock.ssql.util;

import java.util.ArrayList;

public class DoubleSequentialIntMap {

	protected int rowKeySize = 16;
	protected int rowKeyCapacity = 0;
	protected int[] rowKeys;
	protected int rowValueSize = 8;
	protected int[] rowValueSizes;
	protected int[] rowValueCapacities;
	protected int[][] rowValues;
	
	protected int columnKeySize = 16;
	protected int columnKeyCapacity = 0;
	protected int[] columnKeys;
	protected int columnValueSize = 8;
	protected int[] columnValueSizes;
	protected int[] columnValueCapacities;
	protected int[][] columnValues;
	
	public DoubleSequentialIntMap() {
		rowKeys = new int[rowKeySize];
		rowValues = new int[rowKeySize][];
		rowValueSizes = new int[rowKeySize];
		for (int i = 0; i < rowKeySize; i++) {
			rowValueSizes[i] = rowValueSize;
		}
		rowValueCapacities = new int[rowKeySize];
		
		columnKeys = new int[columnKeySize];
		columnValues = new int[columnKeySize][];
		columnValueSizes = new int[columnKeySize];
		for (int i = 0; i < columnKeySize; i++) {
			columnValueSizes[i] = columnValueSize;
		}
		columnValueCapacities = new int[columnKeySize];
	}
	
//	public DoubleSequentialIntMap(int rowKeySize) {
//		this.rowKeySize = rowKeySize;
//		rowKeys = new int[rowKeySize];
//		rowValueSizes = new int[rowKeySize];
//		for (int i = 0; i < rowKeySize; i++) {
//			rowValueSizes[i] = rowValueSize;
//		}
//		rowValues = new int[rowKeySize][];
//		rowValueCapacities = new int[rowKeySize];
//	}
	
	//insert a whole row
	public void insertRow(int key, int[] value, int vs, int vc) {
		if (rowKeyCapacity == rowKeySize) {
			rowKeySize = 2 * rowKeySize;
			int[] temp1 = rowKeys;
			rowKeys = new int[rowKeySize];
			int[][] temp2 = rowValues;
			rowValues = new int[rowKeySize][];
			int[] temp3 = rowValueSizes;
			rowValueSizes = new int[rowKeySize];
			int[] temp4 = rowValueCapacities;
			rowValueCapacities = new int[rowKeySize];
			for (int i = 0; i < temp1.length; i++) {
				rowKeys[i] = temp1[i];
				rowValues[i] = temp2[i];
				rowValueSizes[i] = temp3[i];
				rowValueCapacities[i] = temp4[i];
			}
			for (int i = temp1.length; i < rowKeySize; i++) {
				rowValueSizes[i] = rowValueSize;
			}
		}
		
		rowKeys[rowKeyCapacity] = key;
		rowValues[rowKeyCapacity] = value;
		rowValueSizes[rowKeyCapacity] = vs;
		rowValueCapacities[rowKeyCapacity] = vc;
		
		for (int i = 0; i < rowValueCapacities[rowKeyCapacity]; i++) {
			this.insertToColumn(key, rowValues[rowKeyCapacity][i]);
		}
		
		rowKeyCapacity++;
	}
	
	//insert a whole column
	public void insertColumn(int key, int[] value, int vs, int vc) {
		if (columnKeyCapacity == columnKeySize) {
			columnKeySize = 2 * columnKeySize;
			int[] temp1 = columnKeys;
			columnKeys = new int[columnKeySize];
			int[][] temp2 = columnValues;
			columnValues = new int[columnKeySize][];
			int[] temp3 = columnValueSizes;
			columnValueSizes = new int[columnKeySize];
			int[] temp4 = columnValueCapacities;
			columnValueCapacities = new int[columnKeySize];
			for (int i = 0; i < temp1.length; i++) {
				columnKeys[i] = temp1[i];
				columnValues[i] = temp2[i];
				columnValueSizes[i] = temp3[i];
				columnValueCapacities[i] = temp4[i];
			}
			for (int i = temp1.length; i < columnKeySize; i++) {
				columnValueSizes[i] = columnValueSize;
			}
		}
		
		columnKeys[columnKeyCapacity] = key;
		columnValues[columnKeyCapacity] = value;
		columnValueSizes[columnKeyCapacity] = vs;
		columnValueCapacities[columnKeyCapacity] = vc;
		
		for (int i = 0; i < columnValueCapacities[columnKeyCapacity]; i++) {
			this.insertToRow(columnValues[columnKeyCapacity][i], key);
		}
		
		columnKeyCapacity++;
	}
	
	public void doubleInsert(int row, int column) {
		this.insertToRow(row, column);
		this.insertToColumn(row, column);
	}
	
	public void insertToRow(int row, int column) {
		//initialize the parameters
		int low = 0;
		int mid = 0;
		int top = rowKeyCapacity - 1;
		
		//find the position to insert
		while (low <= top) {
			mid = (low + top) / 2;
			if (row < rowKeys[mid]) {
				top = mid - 1;
			}
			else if (row > rowKeys[mid]) {
				low = mid + 1;
			}
			else if (row == rowKeys[mid]) {
				//the key exists, append the value
				this.appendRowValue(mid, column);
				return;
			}
		}
		
		//if the capacity is full, expand the size
		if (rowKeyCapacity == rowKeySize) {
			rowKeySize = 2 * rowKeySize;
			int[] temp1 = rowKeys;
			rowKeys = new int[rowKeySize];
			int[][] temp2 = rowValues;
			rowValues = new int[rowKeySize][];
			int[] temp3 = rowValueSizes;
			rowValueSizes = new int[rowKeySize];
			int[] temp4 = rowValueCapacities;
			rowValueCapacities = new int[rowKeySize];
			for (int i = 0; i < temp1.length; i++) {
				rowKeys[i] = temp1[i];
				rowValues[i] = temp2[i];
				rowValueSizes[i] = temp3[i];
				rowValueCapacities[i] = temp4[i];
			}
			for (int i = temp1.length; i < rowKeySize; i++) {
				rowValueSizes[i] = rowValueSize;
			}
		}
		
		//if the key is to be inserted on a not-empty position, move keys and make some room
		for (int i = rowKeyCapacity; i > low; i--) {
			rowKeys[i] = rowKeys[i - 1];
			rowValues[i] = rowValues[i - 1];
			rowValueSizes[i] = rowValueSizes[i - 1];
			rowValueCapacities[i] = rowValueCapacities[i - 1];
		}
		
		//insert
		rowKeys[low] = row;
		rowValues[low] = new int[rowValueSize];
		rowValues[low][0] = column;
		
		//renew the parameters
		rowKeyCapacity++;
		rowValueSizes[low] = rowValueSize;
		rowValueCapacities[low] = 1;
	}
	
	public void insertToColumn(int row, int column) {
		//initialize the parameters
		int low = 0;
		int mid = 0;
		int top = columnKeyCapacity - 1;
		
		//find the position to insert
		while (low <= top) {
			mid = (low + top) / 2;
			if (column < columnKeys[mid]) {
				top = mid - 1;
			}
			else if (column > columnKeys[mid]) {
				low = mid + 1;
			}
			else if (column == columnKeys[mid]) {
				//the key exists, append the value
				this.appendColumnValue(mid, row);
				return;
			}
		}
		
		//if the capacity is full, expand the size
		if (columnKeyCapacity == columnKeySize) {
			columnKeySize = 2 * columnKeySize;
			int[] temp1 = columnKeys;
			columnKeys = new int[columnKeySize];
			int[][] temp2 = columnValues;
			columnValues = new int[columnKeySize][];
			int[] temp3 = columnValueSizes;
			columnValueSizes = new int[columnKeySize];
			int[] temp4 = columnValueCapacities;
			columnValueCapacities = new int[columnKeySize];
			for (int i = 0; i < temp1.length; i++) {
				columnKeys[i] = temp1[i];
				columnValues[i] = temp2[i];
				columnValueSizes[i] = temp3[i];
				columnValueCapacities[i] = temp4[i];
			}
			for (int i = temp1.length; i < columnKeySize; i++) {
				columnValueSizes[i] = columnValueSize;
			}
		}
		
		//if the key is to be inserted on a not-empty position, move keys and make some room
		for (int i = columnKeyCapacity; i > low; i--) {
			columnKeys[i] = columnKeys[i - 1];
			columnValues[i] = columnValues[i - 1];
			columnValueSizes[i] = columnValueSizes[i - 1];
			columnValueCapacities[i] = columnValueCapacities[i - 1];
		}
		
		//insert
		columnKeys[low] = column;
		columnValues[low] = new int[columnValueSize];
		columnValues[low][0] = row;
		
		//renew the parameters
		columnKeyCapacity++;
		columnValueSizes[low] = columnValueSize;
		columnValueCapacities[low] = 1;
	}
	
	public void appendRowValue(int pos, int value) {
		if (rowValueCapacities[pos] == rowValueSizes[pos]) {
			rowValueSizes[pos] = 2 * rowValueSizes[pos];
			int[] temp = rowValues[pos];
			rowValues[pos] = new int[rowValueSizes[pos]];
			for (int i = 0; i < temp.length; i++) {
				rowValues[pos][i] = temp[i];
			}
		}
		
		rowValues[pos][rowValueCapacities[pos]] = value;
		rowValueCapacities[pos]++;
	}
	
	public void appendColumnValue(int pos, int value) {
		if (columnValueCapacities[pos] == columnValueSizes[pos]) {
			columnValueSizes[pos] = 2 * columnValueSizes[pos];
			int[] temp = columnValues[pos];
			columnValues[pos] = new int[columnValueSizes[pos]];
			for (int i = 0; i < temp.length; i++) {
				columnValues[pos][i] = temp[i];
			}
		}
		
		columnValues[pos][columnValueCapacities[pos]] = value;
		columnValueCapacities[pos]++;
	}
	
	public int getRowKeySize() {
		return rowKeySize;
	}

	public int getRowKeyCapacity() {
		return rowKeyCapacity;
	}

	public int getRowKey(int index) {
		return rowKeys[index];
	}

	public int getRowValueSize(int index) {
		return rowValueSizes[index];
	}

	public int getRowValueCapacity(int index) {
		return rowValueCapacities[index];
	}

	public int[] getRowValues(int index) {
		return rowValues[index];
	}

	public int getColumnKeyCapacity() {
		return columnKeyCapacity;
	}

	public int getColumnKey(int index) {
		return columnKeys[index];
	}

	public int getColumnValueSize(int index) {
		return columnValueSizes[index];
	}

	public int getColumnValueCapacity(int index) {
		return columnValueCapacities[index];
	}

	public int[] getColumnValues(int index) {
		return columnValues[index];
	}
	
	public void print() {
		for (int i = 0; i < rowKeyCapacity; i++) {
			for (int j = 0; j < rowValueCapacities[i]; j++) {
				System.out.println(rowKeys[i] + "-->" + rowValues[i][j]);
			}
		}
	}
	
	public ArrayList<String> output() {
		ArrayList<String> result = new ArrayList<String>();
		for (int i = 0; i < rowKeyCapacity; i++) {
			for (int j = 0; j < rowValueCapacities[i]; j++) {
				result.add(rowKeys[i] + "-->" + rowValues[i][j]);
			}
		}
		return result;
	}
	
}
